import { defineComponent, App, computed, nextTick, ref, ComponentPublicInstance } from 'vue'
import { isArray } from '@vue/shared'
import { Arrayable } from '@vueuse/core'
import moment, { Moment } from 'moment'
import useMemo from '../_util/hooks/useMemo'
import { castArray } from '../_util/_'

import { formatDate, toMoment } from './utils'
import { PickerProps } from './props'

import Trigger from '../trigger'
import Icon from '../icon'
import Input from '../input'
import Calendar from '../calendar'
import RangeCalendar from '../calendar/RangeCalendar'

const prefixCls = 'x-date-picker'

const DatePicker = defineComponent({
  name: prefixCls,
  props: PickerProps,
  emits: ['update:value', 'change'],
  setup(props, { emit }) {
    const calendarRef = ref<ComponentPublicInstance>()
    const inputRef = ref<ComponentPublicInstance>()

    const visible = ref(false)
    const valueRef = useMemo(() => toMoment(props.value, props.valueFormat) as Arrayable<Moment>)
    const isRange = computed(() => props.type.includes('range'))

    function onChange(date: Arrayable<Moment | null>) {
      valueRef.value = isRange.value ? castArray(date).map(e => e?.clone()) : date?.clone()
      if (isArray(date)) {
        const val = props.valueFormat ? formatDate(date, props.valueFormat) : date?.map(e => e.clone())
        emit('update:value', val)
        emit('change', val)
      } else {
        const val = props.valueFormat ? formatDate(date, props.valueFormat) : date?.clone()
        emit('update:value', val)
        emit('change', val)
      }
    }

    function onSelect(date: Arrayable<Moment>) {
      console.log('date-picker')
      onChange(date)
      visible.value = false
    }
    function onInput(str: Arrayable<string>) {
      if (!str) return onChange(null)
      const date = toMoment(str, props.format, true)
      if (castArray(date).every(e => e.isValid())) {
        onChange(date)
      }
    }
    function onKeydown(e: KeyboardEvent) {
      if (e.key === 'Enter') visible.value = !visible.value
    }
    function onBlur() {
      const temp = valueRef.value
      valueRef.value = (castArray(valueRef.value)[0] ?? moment()).clone().add(1, 'days')
      nextTick(() => (valueRef.value = temp))
      //
      const els = [calendarRef, inputRef].map(e => e.value.$el).filter(e => e) as HTMLElement[]
      setTimeout(() => (visible.value = els.some(e => e == document.activeElement || e.contains(document.activeElement))), 0)
    }

    function renderCalendar() {
      const calendarProps = {
        ref: calendarRef,
        value: valueRef.value,
        disabledDate: props.disabledDate,
        tabindex: -1,
        onSelect,
        onBlur
      }
      // @ts-ignore
      return isRange.value ? <RangeCalendar {...calendarProps}></RangeCalendar> : <Calendar {...calendarProps}></Calendar>
    }

    function renderInput() {
      const { clearable, disabled, placeholder } = props
      const inputProps = {
        ref: inputRef,
        value: formatDate(valueRef.value, props.format),
        clearable,
        disabled,
        placeholder,
        pair: isRange.value,
        onInput,
        onKeydown
      }
      return <Input {...inputProps} class={`${prefixCls}-range`} v-slots={{ suffix: () => <Icon type='calendar' /> }} />
    }

    return () => {
      const { disabled } = props

      const triggerProps = {
        prefixCls: `${prefixCls}-popup`,
        popup: renderCalendar(),
        popupVisible: visible.value,
        action: disabled ? '' : 'click',
        popupAlign: {
          points: ['tl', 'bl']
        },
        onFocus: () => (visible.value = true),
        onBlur
      } as any

      return <Trigger {...triggerProps}>{renderInput()}</Trigger>
    }
  }
})

DatePicker.install = (app: App) => {
  app.component(DatePicker.name, DatePicker)
}

export default DatePicker
